package grape.workflow.rest.task.mvc;

import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.fasterxml.jackson.databind.node.ObjectNode;
import grape.common.exception.ExceptionTools;
import grape.common.exception.runtime.InvalidParamsException;
import grape.common.rest.form.BasePageForm;
import grape.workflow.rest.common.mvc.BaseController;
import grape.workflow.rest.flowable.ext.CustomFlowableTaskQueryService;
import grape.workflow.rest.task.form.DynamicFormCompleteTaskWithFormForm;
import grape.workflow.rest.task.form.RelationFormCompleteTaskWithFormForm;
import grape.workflow.rest.task.form.*;
import grape.workflow.rest.task.mapper.TaskWebMapper;
import grape.workflow.rest.task.vo.TaskRepresentationVo;
import grape.workflow.rest.task.vo.TaskVo;
import grape.workflow.service.comment.api.IWorkflowCommentService;
import grape.workflow.service.comment.dto.CommentDto;
import grape.workflow.service.comment.query.CommentQuery;
import grape.workflow.service.task.dto.TaskDto;
import grape.workflow.service.task.api.IWorkflowTaskService;
import grape.workflow.service.task.query.TaskQuery;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import org.flowable.common.engine.api.FlowableObjectNotFoundException;
import org.flowable.engine.FormService;
import org.flowable.engine.HistoryService;
import org.flowable.engine.RuntimeService;
import org.flowable.engine.TaskService;
import org.flowable.engine.history.HistoricProcessInstance;
import org.flowable.engine.impl.persistence.entity.CommentEntity;
import org.flowable.engine.runtime.ProcessInstance;
import org.flowable.engine.task.Event;
import org.flowable.form.api.FormInfo;
import org.flowable.form.model.SimpleFormModel;
import org.flowable.task.api.Task;
import org.flowable.task.api.TaskInfo;
import org.flowable.ui.common.model.ResultListDataRepresentation;
import org.flowable.ui.common.service.exception.NotFoundException;
import org.flowable.ui.task.model.runtime.TaskRepresentation;
import org.flowable.ui.task.service.runtime.FlowableTaskActionService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.web.bind.annotation.*;
import springfox.documentation.annotations.ApiIgnore;

import javax.validation.Valid;
import java.util.*;
import java.util.stream.Collectors;

/**
 * Created by yangwei
 * Created at 2020/6/16 17:12
 */
@RestController
@RequestMapping("/workflow/task")
@Api(tags = "流程任务相关接口")
public class TaskController extends BaseController {

    @Autowired
    protected CustomFlowableTaskQueryService taskQueryService;
    @Autowired
    private TaskWebMapper taskWebMapper;
    @Autowired
    protected RuntimeService runtimeService;

    @Autowired
    protected HistoryService historyService;
    @Autowired
    protected FlowableTaskActionService taskActionService;

    @Autowired
    private IWorkflowTaskService iTaskService;
    @Autowired
    protected TaskService taskService;
    @Autowired
    private IWorkflowCommentService iCommentService;
    @Autowired
    private FormService formService;
    @Autowired
    protected org.flowable.form.api.FormService apiFormService;
    /**
     *
     * @param taskToDoForm
     * @return
     */
    @ApiOperation("我的待办任务列表")
    @PreAuthorize("hasAuthority('process:tasks:todo:listPage')")
    @GetMapping(value = "/tasks/todo/listPage")
    @ResponseStatus(HttpStatus.OK)
    public IPage<TaskRepresentationVo> taskToDoPageList(TaskListPageForm taskToDoForm) {
        ObjectNode jsonNodes = handleRequestParam(taskToDoForm);
        // 只查询分配的任务
        jsonNodes.put("assignment", "assignee");
        // 查询任务列表
        ResultListDataRepresentation resultListDataRepresentation = taskQueryService.listTasks(jsonNodes);
        return handleResult(resultListDataRepresentation, taskToDoForm,jsonNodes);
    }
    @ApiOperation("历史任务")
    @PreAuthorize("hasAuthority('process:tasks:history:listPage')")
    @GetMapping(value = "/tasks/history/listPage")
    @ResponseStatus(HttpStatus.OK)
    public IPage<TaskRepresentationVo> taskHistoryPageList(@Valid TaskHistoryListPageForm taskHistoryForm) {
        ObjectNode jsonNodes = newObjectNode();
        jsonNodes.put("processInstanceId", taskHistoryForm.getProcessInstanceId());
        jsonNodes.put("state", "completed");

        handlePage(taskHistoryForm, jsonNodes);
        // 查询任务列表
        ResultListDataRepresentation resultListDataRepresentation = taskQueryService.listTasks(jsonNodes);
        return handleResult(resultListDataRepresentation, taskHistoryForm,jsonNodes);
    }

    /**
     * 我的已办任务列表
     * @param taskDoForm
     * @return
     */
    @ApiOperation("我的已办任务列表")
    @PreAuthorize("hasAuthority('process:tasks:do:listPage')")
    @GetMapping(value = "/tasks/do/listPage")
    @ResponseStatus(HttpStatus.OK)
    public IPage<TaskRepresentationVo> taskDoPageList(TaskListPageForm taskDoForm) {
        ObjectNode jsonNodes = handleRequestParam(taskDoForm);
        jsonNodes.put("state", "completed");
        jsonNodes.put("assignment", "assignee");
        // 查询任务列表
        ResultListDataRepresentation resultListDataRepresentation = taskQueryService.listTasks(jsonNodes);
        return handleResult(resultListDataRepresentation, taskDoForm,jsonNodes);
    }


    /**
     * 待签收任务列表
     * @param taskClaimForm
     * @return
     */
    @ApiOperation("待签收任务列表")
    @PreAuthorize("hasAuthority('process:tasks:claim:listPage')")
    @GetMapping(value = "/tasks/claim//listPage")
    @ResponseStatus(HttpStatus.OK)
    public IPage<TaskRepresentationVo> taskClaimPageList(TaskListPageForm taskClaimForm) {
        ObjectNode jsonNodes = handleRequestParam(taskClaimForm);
        jsonNodes.put("assignment", "candidate");
        // 查询任务列表
        ResultListDataRepresentation resultListDataRepresentation = taskQueryService.listTasks(jsonNodes);
        return handleResult(resultListDataRepresentation, taskClaimForm,jsonNodes);
    }

    /**
     * 无法认领任务列表
     * @param taskUnreachableForm
     * @return
     */
    @ApiOperation("无法认领任务列表")
    @PreAuthorize("hasAuthority('process:tasks:unreachable:listPage')")
    @GetMapping(value = "/tasks/unreachable/listPage")
    @ResponseStatus(HttpStatus.OK)
    public IPage<TaskVo> taskUnreachablePageList(TaskListPageForm taskUnreachableForm) {
        TaskQuery param = taskWebMapper.map(taskUnreachableForm);
        IPage page = new Page(taskUnreachableForm.getCurrent(), taskUnreachableForm.getSize());

        IPage iPage = iTaskService.taskUnreachable(page, param);

        return handleRecods( iPage);
    }
    /**
     * 签收任务
     * @param taskId 任务id
     * @return
     */
    @ApiOperation("签收任务")
    @PreAuthorize("hasAuthority('process:tasks:claim')")
    @PostMapping(value = "/tasks/{taskId}/claim")
    @ResponseStatus(HttpStatus.OK)
    public Boolean taskClaim(@PathVariable String taskId) {
        try {
            taskActionService.claimTask(taskId);
        } catch (NotFoundException e) {
            throw ExceptionTools.dataNotExistRE("任务不存在");
        }
        return true;
    }
    /**
     * 反签收任务
     * @param taskId 任务id
     * @return
     */
    @ApiOperation("反签收任务")
    @PreAuthorize("hasAuthority('process:tasks:unclaim')")
    @PostMapping(value = "/tasks/{taskId}/unclaim")
    @ResponseStatus(HttpStatus.OK)
    public Boolean taskUnclaim(@PathVariable String taskId) {
        try {
            taskService.unclaim(taskId);
        } catch (FlowableObjectNotFoundException e) {
            throw ExceptionTools.dataNotExistRE("任务不存在");
        }
        return true;
    }
    /**
     * 委派（委托）任务
     * @param taskId 任务id
     * @return
     */
    @ApiOperation("委派（委托）任务")
    @PreAuthorize("hasAuthority('process:tasks:delegate')")
    @PostMapping(value = "/tasks/{taskId}/delegate")
    @ResponseStatus(HttpStatus.OK)
    public Boolean taskDelegate(@PathVariable String taskId,@RequestBody @Valid TaskDelegateForm delegateForm,@ApiIgnore String loginUserId) {
        if(isEqual(loginUserId,delegateForm.getUserId())){
            throw new InvalidParamsException("不能将任务委托给自己");
        }
        try {
            taskService.delegateTask(taskId,delegateForm.getUserId());
        } catch (FlowableObjectNotFoundException e) {
            throw ExceptionTools.dataNotExistRE("任务不存在");
        }
        return true;
    }
    /**
     * 委派（委托）任务 解决/归还
     * @param taskId 任务id
     * @return
     */
    @ApiOperation("委派（委托）任务 解决/归还")
    @PreAuthorize("hasAuthority('process:tasks:resolve')")
    @PostMapping(value = "/tasks/{taskId}/resolve")
    @ResponseStatus(HttpStatus.OK)
    public Boolean taskResolve(@PathVariable String taskId) {
        try {
            taskService.resolveTask(taskId);
        } catch (FlowableObjectNotFoundException e) {
            throw ExceptionTools.dataNotExistRE("任务不存在");
        }
        return true;
    }
    /**
     * 指派任务（任务分配）
     * @param taskId 任务id
     * @return
     */
    @ApiOperation("指派任务（任务分配）")
    @PreAuthorize("hasAuthority('process:tasks:assign')")
    @PostMapping(value = "/tasks/{taskId}/assign")
    @ResponseStatus(HttpStatus.OK)
    public Boolean taskAssign(@PathVariable String taskId,@RequestBody @Valid TaskAssignForm assignForm,@ApiIgnore String loginUserId) {
        ObjectNode jsonNodes = newObjectNode();

        jsonNodes.put("assignee", assignForm.getIsAssignMe() ? loginUserId : assignForm.getUserId());
        try {
            taskActionService.assignTask(taskId,jsonNodes);
        } catch (NotFoundException e) {
            throw ExceptionTools.dataNotExistRE("任务不存在");
        }
        return true;
    }
    /**
     * 任务转办
     * @param taskId 任务id
     * @return
     */
    @ApiOperation("任务转办")
    @PreAuthorize("hasAuthority('process:tasks:transfer')")
    @PostMapping(value = "/tasks/{taskId}/transfer")
    @ResponseStatus(HttpStatus.OK)
    public Boolean taskTransfer(@PathVariable String taskId, @RequestBody @Valid TaskTransferForm assignForm, @ApiIgnore String loginUserId) {
        ObjectNode jsonNodes = newObjectNode();

        // 转办一般是从待办列表转办的任务，自己已经有了该任务的待办，所有无需再转给自己
        if(isEqual(loginUserId,assignForm.getUserId())){
            throw new InvalidParamsException("不能将任务转办给自己");
        }
        jsonNodes.put("assignee", assignForm.getUserId());
        try {
            taskActionService.assignTask(taskId,jsonNodes);
        } catch (NotFoundException e) {
            throw ExceptionTools.dataNotExistRE("任务不存在");
        }
        return true;
    }

    @ApiOperation("动态表单的任务完成接口")
    @PreAuthorize("hasAuthority('process:dynamicForm:completeTaskWithForm')")
    @PostMapping(value = "/dynamicForm/completeTaskWithForm")
    @ResponseStatus(HttpStatus.CREATED)
    public Boolean dynamicFormCmpleteTaskWithForm(@RequestBody @Valid DynamicFormCompleteTaskWithFormForm completeTaskWithForm) {
        // 注意这里也没有用runtime的completeTaskWithForm 因为这是内部表单的方式，在有formkey的情况下才可以，动态表单不适合
        if (completeTaskWithForm.getIsComplete()) {
            Task task = taskService.createTaskQuery().taskId(completeTaskWithForm.getTaskId()).singleResult();
            // 添加审批意见
            addTaskComment(completeTaskWithForm, task);
            formService.submitTaskFormData(completeTaskWithForm.getTaskId(),completeTaskWithForm.getVariables());
        }else {
            formService.saveFormData(completeTaskWithForm.getTaskId(),completeTaskWithForm.getVariables());
        }

        return true;
    }
    @ApiOperation("关联/外置表单的任务完成接口")
    @PreAuthorize("hasAuthority('process:relationForm:completeTaskWithForm')")
    @PostMapping(value = "/relationForm/completeTaskWithForm")
    @ResponseStatus(HttpStatus.CREATED)
    public Boolean relationFormCmpleteTaskWithForm(@RequestBody @Valid RelationFormCompleteTaskWithFormForm completeTaskWithForm) {

        FormInfo formInfo = taskService.getTaskFormModel(completeTaskWithForm.getTaskId());
        // Get the form definition
        Task task = taskService.createTaskQuery().taskId(completeTaskWithForm.getTaskId()).singleResult();

        if (completeTaskWithForm.getIsComplete()) {

            // 添加审批意见
            addTaskComment(completeTaskWithForm, task);
            taskService.completeTaskWithForm(completeTaskWithForm.getTaskId(), formInfo.getId(),
                    null, completeTaskWithForm.getVariables());
        }else {
            if (task.getProcessInstanceId() != null) {
                apiFormService.saveFormInstanceByFormDefinitionId(completeTaskWithForm.getVariables(), formInfo.getId(), completeTaskWithForm.getTaskId(),
                        task.getProcessInstanceId(), task.getProcessDefinitionId(), task.getTenantId(), null);

            } else {
                apiFormService.saveFormInstanceWithScopeId(completeTaskWithForm.getVariables(), formInfo.getId(), completeTaskWithForm.getTaskId(),
                        task.getScopeId(), task.getScopeType(), task.getScopeDefinitionId(), task.getTenantId(), null);
            }
        }

        return true;
    }

    @ApiOperation("不带表单的任务完成接口")
    @PreAuthorize("hasAuthority('process:withoutForm:completeTaskWithoutForm')")
    @PostMapping(value = "/withoutForm/completeTaskWithoutForm")
    @ResponseStatus(HttpStatus.CREATED)
    public Boolean completeTaskWithoutForm(@RequestBody @Valid TaskCompleteForm taskCompleteForm) {
        Task task = taskService.createTaskQuery().taskId(taskCompleteForm.getTaskId()).singleResult();
        // 添加审批意见
        addTaskComment(taskCompleteForm, task);
        taskService.complete(taskCompleteForm.getTaskId());

        return true;
    }
    private void addTaskComment(TaskCompleteForm completeForm,Task task){
        if (!isStrEmpty(completeForm.getComment())) {
            taskService.addComment(task.getId(), task.getProcessInstanceId(), completeForm.getComment());
        }
    }
    /**
     * 处理结果转换
     * @param iPage
     * @return
     */
    private IPage<TaskVo> handleRecods(IPage iPage){
        List<TaskDto> records = iPage.getRecords();
        assertPageExist(iPage);

        Set<String> processInstanceIds = records.stream().map(TaskDto::getProcessInstanceId).collect(Collectors.toSet());

        Map<String, ProcessInstance> processInstanceMapMap = new HashMap<>();
        Map<String, HistoricProcessInstance> processHistoryInstanceMapMap = new HashMap<>();
        handleProcessInstance(processInstanceIds, processInstanceMapMap,processHistoryInstanceMapMap);
        Set<String> taskIds = records.stream().map(TaskDto::getTaskId).collect(Collectors.toSet());
        Map<String, CommentDto> commentDtoMap = new HashMap<>();

        handleCommentByTaskIds(taskIds,commentDtoMap);

        List<TaskVo> resultRecords = records.stream().map(item -> {
            TaskVo taskVo =  taskWebMapper.map((TaskDto) item);
            // 流程开始时间
            taskVo.setProcessStartAt(Optional.ofNullable(processInstanceMapMap.get(item.getProcessInstanceId())).map(ProcessInstance::getStartTime).orElse(null));
            taskVo.setProcessStartAt(Optional.ofNullable(processHistoryInstanceMapMap.get(item.getProcessInstanceId())).map(HistoricProcessInstance::getStartTime).orElse(null));
            // 结束时间
            taskVo.setProcessEndAt(Optional.ofNullable(processHistoryInstanceMapMap.get(item.getProcessInstanceId())).map(HistoricProcessInstance::getEndTime).orElse(null));
            taskVo.setFirstCommentMessage(Optional.ofNullable(commentDtoMap.get(taskVo.getTaskId())).map(CommentDto::getMessage).orElse(null));

            return taskVo;
        }).collect(Collectors.toList());
        iPage.setRecords(resultRecords);
        return iPage;
    }
    private IPage<TaskRepresentationVo> handleResult(ResultListDataRepresentation resultListDataRepresentation,BasePageForm pageForm,ObjectNode jsonNode) {
        // 组装分页数据
        Page<TaskRepresentationVo> objectPage = (Page<TaskRepresentationVo>) newPage(resultListDataRepresentation, pageForm);

        List<TaskRepresentation> result = (List<TaskRepresentation>) resultListDataRepresentation.getData();
        if (isEmpty(result)) {
            throw ExceptionTools.dataNotExistRE("数据不存在");
        }
        // 查询businessKey，
        Map<String, ProcessInstance> processInstanceMapMap = new HashMap<>();
        Map<String, HistoricProcessInstance> processHistoryInstanceMapMap = new HashMap<>();
        Map<String, CommentDto> commentDtoMap = new HashMap<>();
        Map<String, ? extends TaskInfo> taskInfoMap = new HashMap<>();
        handleExt(result, jsonNode,processInstanceMapMap,processHistoryInstanceMapMap,commentDtoMap,taskInfoMap);

        // 转vo主要两个目的，添加扩展属性
        List<TaskRepresentationVo> r = handleVo(result, processInstanceMapMap,processHistoryInstanceMapMap,commentDtoMap);


        objectPage.setRecords(r);
        return objectPage;
    }

    @Autowired
    private TaskMoveReverseHelper taskMoveReverseHelper;
    private List<TaskRepresentationVo> handleVo(List<TaskRepresentation> result,
                                                Map<String, ProcessInstance> processInstanceMapMap,
                                                Map<String, HistoricProcessInstance> processHistoryInstanceMapMap,
                                                Map<String, CommentDto> commentDtoMap
    ) {
        // 转vo主要两个目的，添加时间和业务编号
        return result.stream().map(item -> {
                TaskRepresentationVo representationVo = item instanceof TaskRepresentationVo ? ((TaskRepresentationVo) item) : taskWebMapper.map( item);
                // businessKey 设置两遍，可能第一个没有值
                representationVo.setBusinessKey(Optional.ofNullable(processInstanceMapMap.get(representationVo.getProcessInstanceId())).map(ProcessInstance::getBusinessKey).orElse(null));
                representationVo.setBusinessKey(Optional.ofNullable(processHistoryInstanceMapMap.get(representationVo.getProcessInstanceId())).map(HistoricProcessInstance::getBusinessKey).orElse(null));
                // 流程开始时间
            representationVo.setProcessStartAt(Optional.ofNullable(processInstanceMapMap.get(representationVo.getProcessInstanceId())).map(ProcessInstance::getStartTime).orElse(null));
            representationVo.setProcessStartAt(Optional.ofNullable(processHistoryInstanceMapMap.get(representationVo.getProcessInstanceId())).map(HistoricProcessInstance::getStartTime).orElse(null));
            // 结束时间
            representationVo.setProcessEndAt(Optional.ofNullable(processHistoryInstanceMapMap.get(representationVo.getProcessInstanceId())).map(HistoricProcessInstance::getEndTime).orElse(null));
            representationVo.setFirstCommentMessage(Optional.ofNullable(commentDtoMap.get(representationVo.getId())).map(CommentDto::getMessage).orElse(null));
            // 已办加载太慢，这里不再直接返回
            /*if (representationVo.getEndDate() != null) {
                representationVo.setIsCanRecall(taskMoveReverseHelper.isCanRecall(representationVo.getId(),false));
            }*/
                return representationVo;
        }).collect(Collectors.toList());
    }

    private ObjectNode handleRequestParam(TaskListPageForm taskToDoForm) {
        ObjectNode jsonNodes = newObjectNode();
        // 以下魔法变量参见 org.flowable.ui.task.service.runtime.FlowableTaskQueryService.listTasks
        jsonNodes.put("text", taskToDoForm.getTaskName());
        handlePage(taskToDoForm, jsonNodes);

        jsonNodes.put("businessKey", taskToDoForm.getBusinessKey());

        return jsonNodes;
    }

    /**
     * 处理扩展属性
     * @param result
     * @param processInstanceMapMap
     * @param processHistoryInstanceMapMap
     */
    private void handleExt(List<TaskRepresentation> result,ObjectNode jsonNode,
                           Map<String, ProcessInstance> processInstanceMapMap,
                           Map<String, HistoricProcessInstance> processHistoryInstanceMapMap,
                           Map<String, CommentDto> commentDtoMap,
                           Map<String, ? extends TaskInfo> taskInfoMap
    ) {

        Set<String> processInstanceIds = result.stream().map(TaskRepresentation::getProcessInstanceId).collect(Collectors.toSet());
        Set<String> taskIds = result.stream().map(TaskRepresentation::getId).collect(Collectors.toSet());

        handleProcessInstance(processInstanceIds, processInstanceMapMap, processHistoryInstanceMapMap);
        handleCommentByTaskIds(taskIds, commentDtoMap);
    }

    protected void handleCommentByTaskIds(Set<String> taskIds, Map<String, CommentDto> commentDtoMap){
        CommentQuery commentParam = new CommentQuery();
        commentParam.setTaskIds(taskIds);

        commentParam.setType(CommentEntity.TYPE_COMMENT);
        commentParam.setAction(Event.ACTION_ADD_COMMENT);

        List<CommentDto> list = iCommentService.list(commentParam);
        if (!isEmpty(list)) {
            for (CommentDto commentDto : list) {
                commentDtoMap.put(commentDto.getTaskId(), commentDto);
            }
        }
    }
    private void handleProcessInstance(Set<String> processInstanceIds, Map<String, ProcessInstance> processInstanceMapMap,Map<String, HistoricProcessInstance> processHistoryInstanceMapMap){
        List<ProcessInstance> processInstances = runtimeService.createProcessInstanceQuery().processInstanceIds(processInstanceIds).list();
        for (ProcessInstance processInstance : processInstances) {
            processInstanceMapMap.put(processInstance.getId(), processInstance);
        }

        List<HistoricProcessInstance> historicProcessInstances = historyService.createHistoricProcessInstanceQuery().processInstanceIds(processInstanceIds).list();
        for (HistoricProcessInstance historicProcessInstance : historicProcessInstances) {
            processHistoryInstanceMapMap.put(historicProcessInstance.getId(), historicProcessInstance);
        }
    }
}
